#!/usr/bin/env python3
"""
风险管理模块
提供专业的风险评估、止损位计算、仓位管理等功能
"""

import numpy as np
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum
import math


class RiskLevel(Enum):
    """风险等级"""
    VERY_LOW = "极低风险"
    LOW = "低风险"
    MODERATE = "中等风险"
    HIGH = "高风险"
    VERY_HIGH = "极高风险"


class PositionSizeMethod(Enum):
    """仓位计算方法"""
    FIXED_PERCENTAGE = "固定百分比"
    VOLATILITY_BASED = "波动率调整"
    KELLY_FORMULA = "凯利公式"
    ATR_BASED = "ATR止损法"


@dataclass
class RiskMetrics:
    """风险指标"""
    risk_level: RiskLevel
    risk_score: float  # 0-100
    volatility: float  # 年化波动率
    max_drawdown: float  # 最大回撤
    var_95: float  # 95%置信度风险价值
    sharpe_ratio: float  # 夏普比率
    beta: float  # 贝塔系数
    correlation_market: float  # 与市场相关性


@dataclass
class StopLossLevels:
    """止损位设置"""
    technical_stop: float  # 技术止损位
    atr_stop: float  # ATR止损位
    percentage_stop: float  # 百分比止损位
    support_stop: float  # 支撑位止损
    recommended_stop: float  # 推荐止损位
    stop_loss_pct: float  # 止损百分比


@dataclass
class PositionSizing:
    """仓位管理"""
    recommended_position: float  # 推荐仓位比例
    max_position: float  # 最大仓位比例
    risk_per_trade: float  # 单笔风险比例
    position_value: float  # 建议投资金额
    shares_to_buy: int  # 建议买入股数
    method_used: PositionSizeMethod  # 使用的计算方法


@dataclass
class RiskAssessment:
    """风险评估结果"""
    stock_code: str
    stock_name: str
    current_price: float
    risk_metrics: RiskMetrics
    stop_loss_levels: StopLossLevels
    position_sizing: PositionSizing
    risk_warnings: List[str]
    recommendations: List[str]


class RiskManager:
    """风险管理器"""
    
    def __init__(self, account_value: float = 100000, risk_tolerance: float = 0.02):
        """初始化风险管理器
        
        Args:
            account_value: 账户总价值
            risk_tolerance: 风险承受度 (单笔交易最大损失比例)
        """
        self.account_value = account_value
        self.risk_tolerance = risk_tolerance
        self.market_volatility = 0.15  # 市场年化波动率
        
    def assess_risk(self, stock_code: str, stock_name: str, current_price: float,
                   price_history: List[float], volume_history: List[float],
                   support_levels: List[float] = None,
                   resistance_levels: List[float] = None) -> RiskAssessment:
        """综合风险评估"""
        
        # 计算风险指标
        risk_metrics = self._calculate_risk_metrics(price_history)
        
        # 计算止损位
        stop_loss_levels = self._calculate_stop_loss_levels(
            current_price, price_history, support_levels
        )
        
        # 计算仓位大小
        position_sizing = self._calculate_position_sizing(
            current_price, stop_loss_levels.recommended_stop, risk_metrics.volatility
        )
        
        # 生成风险警告
        risk_warnings = self._generate_risk_warnings(risk_metrics, price_history)
        
        # 生成建议
        recommendations = self._generate_recommendations(
            risk_metrics, stop_loss_levels, position_sizing
        )
        
        return RiskAssessment(
            stock_code=stock_code,
            stock_name=stock_name,
            current_price=current_price,
            risk_metrics=risk_metrics,
            stop_loss_levels=stop_loss_levels,
            position_sizing=position_sizing,
            risk_warnings=risk_warnings,
            recommendations=recommendations
        )
    
    def _calculate_risk_metrics(self, price_history: List[float]) -> RiskMetrics:
        """计算风险指标"""
        if len(price_history) < 20:
            return RiskMetrics(
                risk_level=RiskLevel.MODERATE,
                risk_score=50.0,
                volatility=0.20,
                max_drawdown=0.10,
                var_95=0.05,
                sharpe_ratio=0.0,
                beta=1.0,
                correlation_market=0.5
            )
        
        prices = np.array(price_history)
        returns = np.diff(prices) / prices[:-1]
        
        # 计算波动率（年化）
        daily_volatility = np.std(returns)
        annual_volatility = daily_volatility * np.sqrt(252)
        
        # 计算最大回撤
        cumulative = np.cumprod(1 + returns)
        running_max = np.maximum.accumulate(cumulative)
        drawdown = (cumulative - running_max) / running_max
        max_drawdown = abs(np.min(drawdown))
        
        # 计算VaR (95%置信度)
        var_95 = abs(np.percentile(returns, 5))
        
        # 计算夏普比率（简化）
        excess_returns = returns - 0.03/252  # 假设无风险利率3%
        sharpe_ratio = np.mean(excess_returns) / np.std(returns) * np.sqrt(252) if np.std(returns) > 0 else 0
        
        # 计算贝塔系数（简化，假设与市场相关性）
        market_returns = np.random.normal(0.08/252, self.market_volatility/np.sqrt(252), len(returns))
        beta = np.cov(returns, market_returns)[0, 1] / np.var(market_returns) if np.var(market_returns) > 0 else 1.0
        
        # 计算与市场相关性
        correlation_market = np.corrcoef(returns, market_returns)[0, 1] if len(returns) > 1 else 0.5
        
        # 综合风险评分
        risk_score = self._calculate_risk_score(annual_volatility, max_drawdown, var_95)
        
        # 确定风险等级
        risk_level = self._determine_risk_level(risk_score)
        
        return RiskMetrics(
            risk_level=risk_level,
            risk_score=risk_score,
            volatility=annual_volatility,
            max_drawdown=max_drawdown,
            var_95=var_95,
            sharpe_ratio=sharpe_ratio,
            beta=beta,
            correlation_market=correlation_market
        )
    
    def _calculate_stop_loss_levels(self, current_price: float, price_history: List[float],
                                   support_levels: List[float] = None) -> StopLossLevels:
        """计算各种止损位"""
        
        # 技术止损位（基于最近低点）
        recent_prices = price_history[-20:] if len(price_history) >= 20 else price_history
        technical_stop = min(recent_prices) * 0.98
        
        # ATR止损位
        atr = self._calculate_atr(price_history)
        atr_stop = current_price - 2 * atr
        
        # 百分比止损位（5%和10%）
        percentage_stop_5 = current_price * 0.95
        percentage_stop_10 = current_price * 0.90
        
        # 支撑位止损
        if support_levels:
            support_stop = max([s for s in support_levels if s < current_price], default=current_price * 0.90)
        else:
            support_stop = current_price * 0.92
        
        # 推荐止损位（综合考虑）
        stop_candidates = [technical_stop, atr_stop, percentage_stop_5, support_stop]
        stop_candidates = [s for s in stop_candidates if s > 0 and s < current_price]
        
        if stop_candidates:
            recommended_stop = max(stop_candidates)  # 选择最高的止损位，更保守
        else:
            recommended_stop = current_price * 0.95
        
        stop_loss_pct = (current_price - recommended_stop) / current_price
        
        return StopLossLevels(
            technical_stop=technical_stop,
            atr_stop=atr_stop,
            percentage_stop=percentage_stop_5,
            support_stop=support_stop,
            recommended_stop=recommended_stop,
            stop_loss_pct=stop_loss_pct
        )
    
    def _calculate_position_sizing(self, current_price: float, stop_loss: float,
                                  volatility: float) -> PositionSizing:
        """计算仓位大小"""
        
        # 计算单笔风险金额
        risk_amount = self.account_value * self.risk_tolerance
        
        # 计算每股风险
        risk_per_share = current_price - stop_loss
        
        if risk_per_share <= 0:
            risk_per_share = current_price * 0.05  # 默认5%风险
        
        # 基于风险的仓位计算
        shares_by_risk = int(risk_amount / risk_per_share)
        position_value_by_risk = shares_by_risk * current_price
        position_pct_by_risk = position_value_by_risk / self.account_value
        
        # 基于波动率的仓位调整
        volatility_factor = min(1.0, 0.20 / volatility)  # 目标波动率20%
        adjusted_position_pct = position_pct_by_risk * volatility_factor
        
        # 最大仓位限制
        max_position_pct = min(0.20, 1.0 / max(1, volatility * 5))  # 最大20%，高波动率降低
        
        # 最终推荐仓位
        recommended_position_pct = min(adjusted_position_pct, max_position_pct)
        recommended_position_value = self.account_value * recommended_position_pct
        recommended_shares = int(recommended_position_value / current_price)
        
        return PositionSizing(
            recommended_position=recommended_position_pct,
            max_position=max_position_pct,
            risk_per_trade=self.risk_tolerance,
            position_value=recommended_position_value,
            shares_to_buy=recommended_shares,
            method_used=PositionSizeMethod.VOLATILITY_BASED
        )
    
    def _calculate_atr(self, price_history: List[float], period: int = 14) -> float:
        """计算ATR"""
        if len(price_history) < period + 1:
            return np.std(price_history) if price_history else 0.0
        
        # 简化ATR计算，使用价格波动
        prices = np.array(price_history)
        high_low = np.diff(prices)
        atr = np.mean(np.abs(high_low[-period:]))
        
        return atr
    
    def _calculate_risk_score(self, volatility: float, max_drawdown: float, var_95: float) -> float:
        """计算综合风险评分"""
        # 波动率评分 (0-40分)
        vol_score = min(40, volatility * 200)  # 20%波动率 = 40分
        
        # 最大回撤评分 (0-30分)
        drawdown_score = min(30, max_drawdown * 300)  # 10%回撤 = 30分
        
        # VaR评分 (0-30分)
        var_score = min(30, var_95 * 600)  # 5% VaR = 30分
        
        total_score = vol_score + drawdown_score + var_score
        return min(100, total_score)
    
    def _determine_risk_level(self, risk_score: float) -> RiskLevel:
        """确定风险等级"""
        if risk_score <= 20:
            return RiskLevel.VERY_LOW
        elif risk_score <= 40:
            return RiskLevel.LOW
        elif risk_score <= 60:
            return RiskLevel.MODERATE
        elif risk_score <= 80:
            return RiskLevel.HIGH
        else:
            return RiskLevel.VERY_HIGH
    
    def _generate_risk_warnings(self, risk_metrics: RiskMetrics, price_history: List[float]) -> List[str]:
        """生成风险警告"""
        warnings = []
        
        if risk_metrics.risk_level in [RiskLevel.HIGH, RiskLevel.VERY_HIGH]:
            warnings.append(f"⚠️ {risk_metrics.risk_level.value}，建议谨慎操作")
        
        if risk_metrics.volatility > 0.30:
            warnings.append(f"⚠️ 高波动率({risk_metrics.volatility:.1%})，价格波动较大")
        
        if risk_metrics.max_drawdown > 0.20:
            warnings.append(f"⚠️ 历史最大回撤{risk_metrics.max_drawdown:.1%}，存在较大下跌风险")
        
        if risk_metrics.beta > 1.5:
            warnings.append(f"⚠️ 高贝塔系数({risk_metrics.beta:.2f})，对市场变化敏感")
        
        if risk_metrics.sharpe_ratio < 0:
            warnings.append("⚠️ 夏普比率为负，风险调整后收益不佳")
        
        # 检查价格趋势
        if len(price_history) >= 10:
            recent_change = (price_history[-1] - price_history[-10]) / price_history[-10]
            if recent_change < -0.15:
                warnings.append("⚠️ 近期价格下跌超过15%，趋势偏弱")
        
        return warnings
    
    def _generate_recommendations(self, risk_metrics: RiskMetrics, stop_loss_levels: StopLossLevels,
                                position_sizing: PositionSizing) -> List[str]:
        """生成投资建议"""
        recommendations = []
        
        # 仓位建议
        if position_sizing.recommended_position < 0.05:
            recommendations.append("💡 建议小仓位试探，控制风险")
        elif position_sizing.recommended_position > 0.15:
            recommendations.append("💡 可考虑较大仓位，但需严格止损")
        else:
            recommendations.append("💡 建议中等仓位，平衡收益与风险")
        
        # 止损建议
        recommendations.append(f"🛡️ 建议止损位：¥{stop_loss_levels.recommended_stop:.2f} ({stop_loss_levels.stop_loss_pct:.1%})")
        
        # 风险等级建议
        if risk_metrics.risk_level == RiskLevel.VERY_LOW:
            recommendations.append("📈 低风险股票，适合稳健投资者")
        elif risk_metrics.risk_level == RiskLevel.HIGH:
            recommendations.append("⚡ 高风险股票，适合激进投资者，需严格风控")
        elif risk_metrics.risk_level == RiskLevel.VERY_HIGH:
            recommendations.append("🚨 极高风险，建议观望或极小仓位")
        
        # 波动率建议
        if risk_metrics.volatility > 0.25:
            recommendations.append("📊 高波动率，可考虑分批建仓，降低成本")
        
        # 相关性建议
        if abs(risk_metrics.correlation_market) > 0.8:
            recommendations.append("🔗 与市场高度相关，注意系统性风险")
        
        return recommendations


def create_risk_manager(account_value: float = 100000, risk_tolerance: float = 0.02) -> RiskManager:
    """创建风险管理器"""
    return RiskManager(account_value, risk_tolerance) 